{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lLGpv1fLLIjF"
   },
   "source": [
    "# Function calls\n",
    "\n",
    "Tools are means LLM can use to interact with the world beyond of its internal knowledge. Technically speaking, retrievers are tools to help LLM to get more relevant context, and memory is a tool for LLM to carry out a conversation. Deciding when, which, and how to use a tool, and even to creating a tool is an agentic behavior: Function calls is a process of showing LLM a list of funciton definitions and prompt it to choose one or few of them. Many places use tools and function calls interchangably.\n",
    "\n",
    "In this notebook we will covert function calls, including:\n",
    "\n",
    "- Function call walkthrough\n",
    "\n",
    "- Overall design\n",
    "\n",
    "- Function call in action\n",
    "\n",
    "It follows the tutorial here: https://adalflow.sylph.ai/tutorials/tool_helper.html#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "id": "sfKEfaYC3Go7"
   },
   "outputs": [],
   "source": [
    "from IPython.display import clear_output\n",
    "\n",
    "!pip install -U adalflow[openai,groq,faiss-cpu]\n",
    "\n",
    "clear_output()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip uninstall httpx anyio -y\n",
    "!pip install \"anyio>=3.1.0,<4.0\"\n",
    "!pip install httpx==0.24.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "-4c_AGBt3PlR",
    "outputId": "21a26437-9f95-4478-84e9-ba4369956b6f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Please enter your OpenAI API key: ··········\n",
      "Please enter your GROQ API key: ··········\n",
      "API keys have been set.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from getpass import getpass\n",
    "\n",
    "# Prompt user to enter their API keys securely\n",
    "openai_api_key = getpass(\"Please enter your OpenAI API key: \")\n",
    "groq_api_key = getpass(\"Please enter your GROQ API key: \")\n",
    "\n",
    "# Set environment variables\n",
    "os.environ[\"OPENAI_API_KEY\"] = openai_api_key\n",
    "os.environ[\"GROQ_API_KEY\"] = groq_api_key\n",
    "\n",
    "print(\"API keys have been set.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "GMKuuP7xR9Nt"
   },
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import List\n",
    "import numpy as np\n",
    "import time\n",
    "import asyncio\n",
    "\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers.\"\"\"\n",
    "    time.sleep(1)\n",
    "    return a * b\n",
    "\n",
    "\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\"Add two numbers.\"\"\"\n",
    "    time.sleep(1)\n",
    "    return a + b\n",
    "\n",
    "\n",
    "async def divide(a: float, b: float) -> float:\n",
    "    \"\"\"Divide two numbers.\"\"\"\n",
    "    await asyncio.sleep(1)\n",
    "    return float(a) / b\n",
    "\n",
    "\n",
    "async def search(query: str) -> List[str]:\n",
    "    \"\"\"Search for query and return a list of results.\"\"\"\n",
    "    await asyncio.sleep(1)\n",
    "    return [\"result1\" + query, \"result2\" + query]\n",
    "\n",
    "\n",
    "def numpy_sum(arr: np.ndarray) -> float:\n",
    "    \"\"\"Sum the elements of an array.\"\"\"\n",
    "    return np.sum(arr)\n",
    "\n",
    "\n",
    "x = 2\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Point:\n",
    "    x: int\n",
    "    y: int\n",
    "\n",
    "\n",
    "def add_points(p1: Point, p2: Point) -> Point:\n",
    "    return Point(p1.x + p2.x, p1.y + p2.y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jCA7HMjtT16P"
   },
   "source": [
    "## Function Tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "fgOEoLoDSBqh",
    "outputId": "7e636e2c-9a5d-44f1-f0fe-fe8a6bea474d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FunctionTool(fn: <function multiply at 0x7c146a57d7e0>, async: False, definition: FunctionDefinition(func_name='multiply', func_desc='multiply(a: int, b: int) -> int\\nMultiply two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']}))\n",
      "FunctionTool(fn: <function add at 0x7c146a57d2d0>, async: False, definition: FunctionDefinition(func_name='add', func_desc='add(a: int, b: int) -> int\\nAdd two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']}))\n",
      "FunctionTool(fn: <function divide at 0x7c146a57d1b0>, async: True, definition: FunctionDefinition(func_name='divide', func_desc='divide(a: float, b: float) -> float\\nDivide two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'float'}, 'b': {'type': 'float'}}, 'required': ['a', 'b']}))\n",
      "FunctionTool(fn: <function search at 0x7c146a57d240>, async: True, definition: FunctionDefinition(func_name='search', func_desc='search(query: str) -> List[str]\\nSearch for query and return a list of results.', func_parameters={'type': 'object', 'properties': {'query': {'type': 'str'}}, 'required': ['query']}))\n",
      "FunctionTool(fn: <function numpy_sum at 0x7c146a57dc60>, async: False, definition: FunctionDefinition(func_name='numpy_sum', func_desc='numpy_sum(arr: numpy.ndarray) -> float\\nSum the elements of an array.', func_parameters={'type': 'object', 'properties': {'arr': {'type': 'ndarray'}}, 'required': ['arr']}))\n",
      "FunctionTool(fn: <function add_points at 0x7c146a57cd30>, async: False, definition: FunctionDefinition(func_name='add_points', func_desc='add_points(p1: __main__.Point, p2: __main__.Point) -> __main__.Point\\nNone', func_parameters={'type': 'object', 'properties': {'p1': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}, 'p2': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}}, 'required': ['p1', 'p2']}))\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.func_tool import FunctionTool\n",
    "\n",
    "functions = [multiply, add, divide, search, numpy_sum, add_points]\n",
    "tools = [FunctionTool(fn=fn) for fn in functions]\n",
    "for tool in tools:\n",
    "    print(tool)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "CYJaHFhGSEzH",
    "outputId": "9ab36c6c-7509-4e7f-ce85-11dae889c8c2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'func_name': 'numpy_sum', 'func_desc': 'numpy_sum(arr: numpy.ndarray) -> float\\nSum the elements of an array.', 'func_parameters': {'type': 'object', 'properties': {'arr': {'type': 'ndarray'}}, 'required': ['arr']}}\n"
     ]
    }
   ],
   "source": [
    "print(tools[-2].definition.to_dict())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "_O4bQgXrSKb6"
   },
   "outputs": [],
   "source": [
    "context_map = {tool.definition.func_name: tool for tool in tools}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "id": "-RgWWMdISL1u"
   },
   "outputs": [],
   "source": [
    "function_name = \"add\"\n",
    "function_to_call = context_map[function_name]\n",
    "function_args = {\"a\": 1, \"b\": 2}\n",
    "function_response = function_to_call.call(**function_args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6CT7Tez1SOai",
    "outputId": "e486d882-9179-4db3-f077-6adfc9fc6579"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ToolManager(Tools: [FunctionTool(fn: <function multiply at 0x7c146a57d7e0>, async: False, definition: FunctionDefinition(func_name='multiply', func_desc='multiply(a: int, b: int) -> int\\nMultiply two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function add at 0x7c146a57d2d0>, async: False, definition: FunctionDefinition(func_name='add', func_desc='add(a: int, b: int) -> int\\nAdd two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function divide at 0x7c146a57d1b0>, async: True, definition: FunctionDefinition(func_name='divide', func_desc='divide(a: float, b: float) -> float\\nDivide two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'float'}, 'b': {'type': 'float'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function search at 0x7c146a57d240>, async: True, definition: FunctionDefinition(func_name='search', func_desc='search(query: str) -> List[str]\\nSearch for query and return a list of results.', func_parameters={'type': 'object', 'properties': {'query': {'type': 'str'}}, 'required': ['query']})), FunctionTool(fn: <function numpy_sum at 0x7c146a57dc60>, async: False, definition: FunctionDefinition(func_name='numpy_sum', func_desc='numpy_sum(arr: numpy.ndarray) -> float\\nSum the elements of an array.', func_parameters={'type': 'object', 'properties': {'arr': {'type': 'ndarray'}}, 'required': ['arr']})), FunctionTool(fn: <function add_points at 0x7c146a57cd30>, async: False, definition: FunctionDefinition(func_name='add_points', func_desc='add_points(p1: __main__.Point, p2: __main__.Point) -> __main__.Point\\nNone', func_parameters={'type': 'object', 'properties': {'p1': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}, 'p2': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}}, 'required': ['p1', 'p2']}))], Additional Context: {})\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.tool_manager import ToolManager\n",
    "\n",
    "tool_manager = ToolManager(tools=functions)\n",
    "print(tool_manager)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jzFqNnN_T-cu"
   },
   "source": [
    "## ToolManager"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JX7MibWiUF3U",
    "outputId": "20707186-5ec3-49a4-d553-c3160c3daa84"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ToolManager(Tools: [FunctionTool(fn: <function multiply at 0x7c146a57d7e0>, async: False, definition: FunctionDefinition(func_name='multiply', func_desc='multiply(a: int, b: int) -> int\\nMultiply two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function add at 0x7c146a57d2d0>, async: False, definition: FunctionDefinition(func_name='add', func_desc='add(a: int, b: int) -> int\\nAdd two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'int'}, 'b': {'type': 'int'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function divide at 0x7c146a57d1b0>, async: True, definition: FunctionDefinition(func_name='divide', func_desc='divide(a: float, b: float) -> float\\nDivide two numbers.', func_parameters={'type': 'object', 'properties': {'a': {'type': 'float'}, 'b': {'type': 'float'}}, 'required': ['a', 'b']})), FunctionTool(fn: <function search at 0x7c146a57d240>, async: True, definition: FunctionDefinition(func_name='search', func_desc='search(query: str) -> List[str]\\nSearch for query and return a list of results.', func_parameters={'type': 'object', 'properties': {'query': {'type': 'str'}}, 'required': ['query']})), FunctionTool(fn: <function numpy_sum at 0x7c146a57dc60>, async: False, definition: FunctionDefinition(func_name='numpy_sum', func_desc='numpy_sum(arr: numpy.ndarray) -> float\\nSum the elements of an array.', func_parameters={'type': 'object', 'properties': {'arr': {'type': 'ndarray'}}, 'required': ['arr']})), FunctionTool(fn: <function add_points at 0x7c146a57cd30>, async: False, definition: FunctionDefinition(func_name='add_points', func_desc='add_points(p1: __main__.Point, p2: __main__.Point) -> __main__.Point\\nNone', func_parameters={'type': 'object', 'properties': {'p1': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}, 'p2': {'type': \"{'type': 'Point', 'properties': {'x': {'type': 'int'}, 'y': {'type': 'int'}}, 'required': ['x', 'y']}\"}}, 'required': ['p1', 'p2']}))], Additional Context: {})\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.tool_manager import ToolManager\n",
    "\n",
    "tool_manager = ToolManager(tools=functions)\n",
    "print(tool_manager)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9Bw2fs--UKX7"
   },
   "source": [
    "## Function Call end-to-end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "id": "TywPQMIVUOqh"
   },
   "outputs": [],
   "source": [
    "template = r\"\"\"<SYS>You have these tools available:\n",
    "{% if tools %}\n",
    "<TOOLS>\n",
    "{% for tool in tools %}\n",
    "{{ loop.index }}.\n",
    "{{tool}}\n",
    "------------------------\n",
    "{% endfor %}\n",
    "</TOOLS>\n",
    "{% endif %}\n",
    "<OUTPUT_FORMAT>\n",
    "{{output_format_str}}\n",
    "</OUTPUT_FORMAT>\n",
    "</SYS>\n",
    "User: {{input_str}}\n",
    "You:\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "-vMajeXoUQ5A",
    "outputId": "ca68601b-e9c8-41c3-a6fa-777f225e68e3"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<SYS>You have these tools available:\n",
      "<TOOLS>\n",
      "1.\n",
      "func_name: multiply\n",
      "func_desc: 'multiply(a: int, b: int) -> int\n",
      "\n",
      "  Multiply two numbers.'\n",
      "func_parameters:\n",
      "  type: object\n",
      "  properties:\n",
      "    a:\n",
      "      type: int\n",
      "    b:\n",
      "      type: int\n",
      "  required:\n",
      "  - a\n",
      "  - b\n",
      "------------------------\n",
      "2.\n",
      "func_name: add\n",
      "func_desc: 'add(a: int, b: int) -> int\n",
      "\n",
      "  Add two numbers.'\n",
      "func_parameters:\n",
      "  type: object\n",
      "  properties:\n",
      "    a:\n",
      "      type: int\n",
      "    b:\n",
      "      type: int\n",
      "  required:\n",
      "  - a\n",
      "  - b\n",
      "------------------------\n",
      "</TOOLS>\n",
      "<OUTPUT_FORMAT>\n",
      "None\n",
      "</OUTPUT_FORMAT>\n",
      "</SYS>\n",
      "User: None\n",
      "You:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.prompt_builder import Prompt\n",
    "\n",
    "prompt = Prompt(template=template)\n",
    "small_tool_manager = ToolManager(tools=tools[:2])\n",
    "\n",
    "renered_prompt = prompt(tools=small_tool_manager.yaml_definitions)\n",
    "print(renered_prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "V9-90IFRUUNT",
    "outputId": "ed2f829e-c656-43c6-a454-8a7c32d5dafe"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<SYS>You have these tools available:\n",
      "<OUTPUT_FORMAT>\n",
      "{\n",
      "    \"name\": \"The name of the function (str) (optional)\",\n",
      "    \"kwargs\": \"The keyword arguments of the function (Optional[Dict[str, object]]) (optional)\"\n",
      "}\n",
      "</OUTPUT_FORMAT>\n",
      "</SYS>\n",
      "User: None\n",
      "You:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.types import Function\n",
    "\n",
    "output_data_class = Function\n",
    "output_format_str = output_data_class.to_json_signature(exclude=[\"thought\", \"args\"])\n",
    "\n",
    "renered_prompt = prompt(output_format_str=output_format_str)\n",
    "print(renered_prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "p3kPMhWaUYT1",
    "outputId": "a3de7117-c3eb-404e-e2e7-8a5187b32f6b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<SYS>You have these tools available:\n",
      "<OUTPUT_FORMAT>\n",
      "{\n",
      "    \"action\": \"FuncName(<kwargs>) Valid function call expression. Example: \\\"FuncName(a=1, b=2)\\\" Follow the data type specified in the function parameters.e.g. for Type object with x,y properties, use \\\"ObjectType(x=1, y=2) (str) (required)\"\n",
      "}\n",
      "</OUTPUT_FORMAT>\n",
      "</SYS>\n",
      "User: None\n",
      "You:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from adalflow.core.types import FunctionExpression\n",
    "\n",
    "output_data_class = FunctionExpression\n",
    "output_format_str = output_data_class.to_json_signature(exclude=[\"thought\"])\n",
    "print(prompt(output_format_str=output_format_str))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "MvGyoUmMUatR",
    "outputId": "e819866b-f6e3-4c88-f9f1-22d725a28865"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Your output should be formatted as a standard JSON instance with the following schema:\n",
      "```\n",
      "{\n",
      "    \"name\": \"The name of the function (str) (optional)\",\n",
      "    \"kwargs\": \"The keyword arguments of the function (Optional[Dict[str, object]]) (optional)\"\n",
      "}\n",
      "```\n",
      "-Make sure to always enclose the JSON output in triple backticks (```). Please do not add anything other than valid JSON output!\n",
      "-Use double quotes for the keys and string values.\n",
      "-DO NOT mistaken the \"properties\" and \"type\" in the schema as the actual fields in the JSON output.\n",
      "-Follow the JSON formatting conventions.\n"
     ]
    }
   ],
   "source": [
    "from adalflow.components.output_parsers import JsonOutputParser\n",
    "\n",
    "func_parser = JsonOutputParser(data_class=Function, exclude_fields=[\"thought\", \"args\"])\n",
    "instructions = func_parser.format_instructions()\n",
    "print(instructions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9W7DiGcpUme5"
   },
   "source": [
    "## Function Output Format"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "id": "z5tNhoruUp6o"
   },
   "outputs": [],
   "source": [
    "from adalflow.core.generator import Generator\n",
    "from adalflow.core.types import ModelClientType\n",
    "\n",
    "model_kwargs = {\"model\": \"gpt-4o-mini\"}\n",
    "prompt_kwargs = {\n",
    "    \"tools\": tool_manager.yaml_definitions,\n",
    "    \"output_format_str\": func_parser.format_instructions(),\n",
    "}\n",
    "generator = Generator(\n",
    "    model_client=ModelClientType.OPENAI(),\n",
    "    model_kwargs=model_kwargs,\n",
    "    template=template,\n",
    "    prompt_kwargs=prompt_kwargs,\n",
    "    output_processors=func_parser,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "9DCukn1SUs_x",
    "outputId": "dcfd952c-0699-4d79-ee6d-a59373e3c75d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "0 Query: add 2 and 3\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='add', args=[], kwargs={'a': 2, 'b': 3})\n",
      "Function output: FunctionOutput(name='add', input=Function(thought=None, name='add', args=(), kwargs={'a': 2, 'b': 3}), parsed_input=None, output=5, error=None)\n",
      "\n",
      "1 Query: search for something\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='search', args=[], kwargs={'query': 'something'})\n",
      "Function output: FunctionOutput(name='search', input=Function(thought=None, name='search', args=(), kwargs={'query': 'something'}), parsed_input=None, output=['result1something', 'result2something'], error=None)\n",
      "\n",
      "2 Query: add points (1, 2) and (3, 4)\n",
      "--------------------------------------------------\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:adalflow.core.func_tool:Error at calling <function add_points at 0x7c146a57cd30>: 'dict' object has no attribute 'x'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Function: Function(thought=None, name='add_points', args=[], kwargs={'p1': {'x': 1, 'y': 2}, 'p2': {'x': 3, 'y': 4}})\n",
      "Function output: FunctionOutput(name='add_points', input=Function(thought=None, name='add_points', args=(), kwargs={'p1': {'x': 1, 'y': 2}, 'p2': {'x': 3, 'y': 4}}), parsed_input=None, output=None, error=\"'dict' object has no attribute 'x'\")\n",
      "\n",
      "3 Query: sum numpy array with arr = np.array([[1, 2], [3, 4]])\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='numpy_sum', args=[], kwargs={'arr': [[1, 2], [3, 4]]})\n",
      "Function output: FunctionOutput(name='numpy_sum', input=Function(thought=None, name='numpy_sum', args=(), kwargs={'arr': [[1, 2], [3, 4]]}), parsed_input=None, output=10, error=None)\n",
      "\n",
      "4 Query: multiply 2 with local variable x\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='multiply', args=[], kwargs={'a': 2, 'b': 'x'})\n",
      "Function output: FunctionOutput(name='multiply', input=Function(thought=None, name='multiply', args=(), kwargs={'a': 2, 'b': 'x'}), parsed_input=None, output='xx', error=None)\n",
      "\n",
      "5 Query: divide 2 by 3\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='divide', args=[], kwargs={'a': 2.0, 'b': 3.0})\n",
      "Function output: FunctionOutput(name='divide', input=Function(thought=None, name='divide', args=(), kwargs={'a': 2.0, 'b': 3.0}), parsed_input=None, output=0.6666666666666666, error=None)\n",
      "\n",
      "6 Query: Add 5 to variable y\n",
      "--------------------------------------------------\n",
      "Function: Function(thought=None, name='add', args=[], kwargs={'a': 5, 'b': 'y'})\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:adalflow.core.func_tool:Error at calling <function add at 0x7c146a57d2d0>: unsupported operand type(s) for +: 'int' and 'str'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Function output: FunctionOutput(name='add', input=Function(thought=None, name='add', args=(), kwargs={'a': 5, 'b': 'y'}), parsed_input=None, output=None, error=\"unsupported operand type(s) for +: 'int' and 'str'\")\n"
     ]
    }
   ],
   "source": [
    "queries = [\n",
    "    \"add 2 and 3\",\n",
    "    \"search for something\",\n",
    "    \"add points (1, 2) and (3, 4)\",\n",
    "    \"sum numpy array with arr = np.array([[1, 2], [3, 4]])\",\n",
    "    \"multiply 2 with local variable x\",\n",
    "    \"divide 2 by 3\",\n",
    "    \"Add 5 to variable y\",\n",
    "]\n",
    "\n",
    "for idx, query in enumerate(queries):\n",
    "    prompt_kwargs = {\"input_str\": query}\n",
    "    print(f\"\\n{idx} Query: {query}\")\n",
    "    print(f\"{'-'*50}\")\n",
    "    try:\n",
    "        result = generator(prompt_kwargs=prompt_kwargs)\n",
    "        # print(f\"LLM raw output: {result.raw_response}\")\n",
    "        func = Function.from_dict(result.data)\n",
    "        print(f\"Function: {func}\")\n",
    "        func_output = tool_manager.execute_func(func)\n",
    "        print(f\"Function output: {func_output}\")\n",
    "    except Exception as e:\n",
    "        print(\n",
    "            f\"Failed to execute the function for query: {query}, func: {result.data}, error: {e}\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "O-sBTPATUwsD"
   },
   "source": [
    "## FunctionExpression Output Format"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "id": "TVRZ44N1UyWg"
   },
   "outputs": [],
   "source": [
    "tool_manager = ToolManager(\n",
    "    tools=functions,\n",
    "    additional_context={\"x\": x, \"y\": 0, \"np.array\": np.array, \"np\": np},\n",
    ")\n",
    "func_parser = JsonOutputParser(data_class=FunctionExpression)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "id": "9h47p4XpU2BC"
   },
   "outputs": [],
   "source": [
    "context = r\"\"\"<CONTEXT>\n",
    "Your function expression also have access to these context:\n",
    "{{context_str}}\n",
    "</CONTEXT>\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "id": "n9Qq7wcOU4X9"
   },
   "outputs": [],
   "source": [
    "async def run_async_function_call(self, generator, tool_manager):\n",
    "    answers = []\n",
    "    start_time = time.time()\n",
    "    tasks = []\n",
    "    for idx, query in enumerate(queries):\n",
    "        tasks.append(self.process_query(idx, query, generator, tool_manager))\n",
    "\n",
    "    results = await asyncio.gather(*tasks)\n",
    "    answers.extend(results)\n",
    "    end_time = time.time()\n",
    "    print(f\"Total time taken: {end_time - start_time :.2f} seconds\")\n",
    "    return answers\n",
    "\n",
    "\n",
    "async def process_query(self, idx, query, generator, tool_manager: ToolManager):\n",
    "    print(f\"\\n{idx} Query: {query}\")\n",
    "    print(f\"{'-'*50}\")\n",
    "    try:\n",
    "        result = generator(prompt_kwargs={\"input_str\": query})\n",
    "        func_expr = FunctionExpression.from_dict(result.data)\n",
    "        print(f\"Function_expr: {func_expr}\")\n",
    "        func = tool_manager.parse_func_expr(func_expr)\n",
    "        func_output = await tool_manager.execute_func_async(func)\n",
    "        print(f\"Function output: {func_output}\")\n",
    "        return func_output\n",
    "    except Exception as e:\n",
    "        print(\n",
    "            f\"Failed to execute the function for query: {query}, func: {result.data}, error: {e}\"\n",
    "        )\n",
    "        return None"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
